#ifndef _WX_SOCKET_H_
#define _WX_SOCKET_H_

#include "wx/defs.h"

#if wxUSE_SOCKETS

// ---------------------------------------------------------------------------
// wxSocket headers
// ---------------------------------------------------------------------------

#include "wx/event.h"
#include "wx/sckaddr.h"
#include "wx/gsocket.h"
#include "wx/list.h"

// ------------------------------------------------------------------------
// Types and constants
// ------------------------------------------------------------------------

enum wxSocketNotify {
  wxSOCKET_INPUT = GSOCK_INPUT,
  wxSOCKET_OUTPUT = GSOCK_OUTPUT,
  wxSOCKET_CONNECTION = GSOCK_CONNECTION,
  wxSOCKET_LOST = GSOCK_LOST
};

enum {
  wxSOCKET_INPUT_FLAG = GSOCK_INPUT_FLAG,
  wxSOCKET_OUTPUT_FLAG = GSOCK_OUTPUT_FLAG,
  wxSOCKET_CONNECTION_FLAG = GSOCK_CONNECTION_FLAG,
  wxSOCKET_LOST_FLAG = GSOCK_LOST_FLAG
};

typedef GSocketEventFlags wxSocketEventFlags;

enum wxSocketError {
  // from GSocket
  wxSOCKET_NOERROR = GSOCK_NOERROR,
  wxSOCKET_INVOP = GSOCK_INVOP,
  wxSOCKET_IOERR = GSOCK_IOERR,
  wxSOCKET_INVADDR = GSOCK_INVADDR,
  wxSOCKET_INVSOCK = GSOCK_INVSOCK,
  wxSOCKET_NOHOST = GSOCK_NOHOST,
  wxSOCKET_INVPORT = GSOCK_INVPORT,
  wxSOCKET_WOULDBLOCK = GSOCK_WOULDBLOCK,
  wxSOCKET_TIMEDOUT = GSOCK_TIMEDOUT,
  wxSOCKET_MEMERR = GSOCK_MEMERR,

  // wxSocket-specific (not yet implemented)
  wxSOCKET_DUMMY
};

enum {
  wxSOCKET_NONE = 0,
  wxSOCKET_NOWAIT = 1,
  wxSOCKET_WAITALL = 2,
  wxSOCKET_BLOCK = 4,
  wxSOCKET_REUSEADDR = 8
};

enum wxSocketType {
  wxSOCKET_UNINIT,
  wxSOCKET_CLIENT,
  wxSOCKET_SERVER,
  wxSOCKET_BASE,
  wxSOCKET_DATAGRAM
};

typedef int wxSocketFlags;



// --------------------------------------------------------------------------
// wxSocketBase
// --------------------------------------------------------------------------

class wxSocketBase : public wxObject {
    DECLARE_CLASS( wxSocketBase )

  public:

    // Public interface
    // ----------------

    // ctors and dtors
    wxSocketBase();
    wxSocketBase( wxSocketFlags flags, wxSocketType type );
    virtual ~wxSocketBase();
    void Init();
    bool Destroy();

    // state
    inline bool Ok() const { return IsOk(); }
    inline bool IsOk() const { return ( m_socket != NULL ); }
    inline bool Error() const { return m_error; }
    inline bool IsConnected() const { return m_connected; }
    inline bool IsData() { return WaitForRead( 0, 0 ); }
    inline bool IsDisconnected() const { return !IsConnected(); }
    inline wxUint32 LastCount() const { return m_lcount; }
    inline wxSocketError LastError() const { return ( wxSocketError )m_socket->GetError(); }
    void SaveState();
    void RestoreState();

    // addresses
    virtual bool GetLocal( wxSockAddress& addr_man ) const;
    virtual bool GetPeer( wxSockAddress& addr_man ) const;
    virtual bool SetLocal( wxIPV4address& local );

    // base IO
    virtual bool  Close();
    wxSocketBase& Discard();
    wxSocketBase& Peek( void* buffer, wxUint32 nbytes );
    wxSocketBase& Read( void* buffer, wxUint32 nbytes );
    wxSocketBase& ReadMsg( void *buffer, wxUint32 nbytes );
    wxSocketBase& Unread( const void *buffer, wxUint32 nbytes );
    wxSocketBase& Write( const void *buffer, wxUint32 nbytes );
    wxSocketBase& WriteMsg( const void *buffer, wxUint32 nbytes );

    void InterruptWait() { m_interrupt = true; }
    bool Wait( long seconds = -1, long milliseconds = 0 );
    bool WaitForRead( long seconds = -1, long milliseconds = 0 );
    bool WaitForWrite( long seconds = -1, long milliseconds = 0 );
    bool WaitForLost( long seconds = -1, long milliseconds = 0 );

    inline wxSocketFlags GetFlags() const { return m_flags; }
    void SetFlags( wxSocketFlags flags );
    void SetTimeout( long seconds );

    bool GetOption( int level, int optname, void *optval, int *optlen );
    bool SetOption( int level, int optname, const void *optval, int optlen );
    inline wxUint32 GetLastIOSize() const { return m_lcount; }

    // event handling
    void *GetClientData() const { return m_clientData; }
    void SetClientData( void *data ) { m_clientData = data; }
    void SetEventHandler( wxEvtHandler& handler, int id = wxID_ANY );
    void SetNotify( wxSocketEventFlags flags );
    void Notify( bool notify );

    // initialize/shutdown the sockets (usually called automatically)
    static bool IsInitialized();
    static bool Initialize();
    static void Shutdown();


    // Implementation from now on
    // --------------------------

    // do not use, should be private (called from GSocket)
    void OnRequest( wxSocketNotify notify );

    // do not use, not documented nor supported
    inline bool IsNoWait() const { return ( ( m_flags & wxSOCKET_NOWAIT ) != 0 ); }
    inline wxSocketType GetType() const { return m_type; }

  private:
    friend class wxSocketClient;
    friend class wxSocketServer;
    friend class wxDatagramSocket;

    // low level IO
    wxUint32 _Read( void* buffer, wxUint32 nbytes );
    wxUint32 _Write( const void *buffer, wxUint32 nbytes );
    bool     _Wait( long seconds, long milliseconds, wxSocketEventFlags flags );

    // pushback buffer
    void     Pushback( const void *buffer, wxUint32 size );
    wxUint32 GetPushback( void *buffer, wxUint32 size, bool peek );

  private:
    // socket
    GSocket      *m_socket;           // GSocket
    wxSocketType  m_type;             // wxSocket type

    // state
    wxSocketFlags m_flags;            // wxSocket flags
    bool          m_connected;        // connected?
    bool          m_establishing;     // establishing connection?
    bool          m_reading;          // busy reading?
    bool          m_writing;          // busy writing?
    bool          m_error;            // did last IO call fail?
    wxSocketError m_lasterror;        // last error (not cleared on success)
    wxUint32      m_lcount;           // last IO transaction size
    unsigned long m_timeout;          // IO timeout value
    wxList        m_states;           // stack of states
    bool          m_interrupt;        // interrupt ongoing wait operations?
    bool          m_beingDeleted;     // marked for delayed deletion?
    wxIPV4address m_localAddress;     // bind to local address?

    // pushback buffer
    void         *m_unread;           // pushback buffer
    wxUint32      m_unrd_size;        // pushback buffer size
    wxUint32      m_unrd_cur;         // pushback pointer (index into buffer)

    // events
    int           m_id;               // socket id
    wxEvtHandler *m_handler;          // event handler
    void         *m_clientData;       // client data for events
    bool          m_notify;           // notify events to users?
    wxSocketEventFlags  m_eventmask;  // which events to notify?

    // the initialization count, GSocket is initialized if > 0
    static size_t m_countInit;

    DECLARE_NO_COPY_CLASS( wxSocketBase )
};

class wxSocketServer : public wxSocketBase {
    DECLARE_CLASS( wxSocketServer )

  public:
    wxSocketServer( const wxSockAddress& addr, wxSocketFlags flags = wxSOCKET_NONE );

    wxSocketBase* Accept( bool wait = true );
    bool AcceptWith( wxSocketBase& socket, bool wait = true );

    bool WaitForAccept( long seconds = -1, long milliseconds = 0 );

    DECLARE_NO_COPY_CLASS( wxSocketServer )
};

class wxSocketClient : public wxSocketBase {
    DECLARE_CLASS( wxSocketClient )

  public:
    wxSocketClient( wxSocketFlags flags = wxSOCKET_NONE );
    virtual ~wxSocketClient();

    virtual bool Connect( wxSockAddress& addr, bool wait = true );
    bool Connect( wxSockAddress& addr, wxSockAddress& local, bool wait = true );

    bool WaitOnConnect( long seconds = -1, long milliseconds = 0 );

  private:
    virtual bool DoConnect( wxSockAddress& addr, wxSockAddress* local, bool wait = true );

    DECLARE_NO_COPY_CLASS( wxSocketClient )
};

class wxDatagramSocket : public wxSocketBase {
    DECLARE_CLASS( wxDatagramSocket )

  public:
    wxDatagramSocket( const wxSockAddress& addr, wxSocketFlags flags = wxSOCKET_NONE );

    wxDatagramSocket& RecvFrom( wxSockAddress& addr,
                                void* buf,
                                wxUint32 nBytes );
    wxDatagramSocket& SendTo( const wxSockAddress& addr,
                              const void* buf,
                              wxUint32 nBytes );
    DECLARE_NO_COPY_CLASS( wxDatagramSocket )
};

class wxSocketEvent : public wxEvent {
  public:
    wxSocketEvent( int id = 0 )
      : wxEvent( id, wxEVT_SOCKET ) {
    }

    wxSocketNotify  GetSocketEvent() const { return m_event; }
    wxSocketBase   *GetSocket() const      { return ( wxSocketBase * ) GetEventObject(); }
    void           *GetClientData() const  { return m_clientData; }

    virtual wxEvent *Clone() const { return new wxSocketEvent( *this ); }

  public:
    wxSocketNotify  m_event;
    void           *m_clientData;

    DECLARE_DYNAMIC_CLASS_NO_ASSIGN( wxSocketEvent )
};


typedef void ( wxEvtHandler::*wxSocketEventFunction )( wxSocketEvent& );

#define wxSocketEventHandler(func) \
  (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(wxSocketEventFunction, &func)

#define EVT_SOCKET(id, func) \
  wx__DECLARE_EVT1(wxEVT_SOCKET, id, wxSocketEventHandler(func))

#endif // wxUSE_SOCKETS

#endif // _WX_SOCKET_H_

